#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "highlighters.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    // EDITOR PATH VARIABLE
    /* This needs to point to the location of editor.html
       the src-noconflict directory from ace-builds needs to be here too
       https://github.com/ajaxorg/ace-builds/
    */
    EditorPath = qApp->applicationDirPath();
    // EditorPath = "/usr/local/share/qaceedit";
    // Setup initial webview
    ui->webView->setUrl(QUrl("file:///"+EditorPath+"/editor.html"));
    ui->centralWidget->setContentsMargins(0,0,0,0);
    // Setup toolbar    
    btnNew = new QToolButton(this);
    btnNew->setText("New");
    connect(btnNew,SIGNAL(clicked()),this,SLOT(NewFileClick()));
    ui->mainToolBar->addWidget(btnNew);
    btnOpen = new QToolButton(this);
    btnOpen->setText("Open");
    connect(btnOpen,SIGNAL(clicked()),this,SLOT(OpenFileClick()));
    ui->mainToolBar->addWidget(btnOpen);
    btnSave = new QToolButton(this);
    btnSave->setText("Save");
    connect(btnSave,SIGNAL(clicked()),this,SLOT(SaveFileClick()));
    ui->mainToolBar->addWidget(btnSave);
    ui->mainToolBar->addSeparator();
    listFonts = new QComboBox(this);
    ui->mainToolBar->addWidget(listFonts);
    textFontSize = new QSpinBox(this);
    ui->mainToolBar->addWidget(textFontSize);
    ui->mainToolBar->addSeparator();
    btnFind = new QToolButton(this);
    btnFind->setText("Find");
    ui->mainToolBar->addWidget(btnFind);
    connect(btnFind,SIGNAL(clicked()),this,SLOT(FindClick()));
    btnReplace = new QToolButton(this);
    btnReplace->setText("Replace");
    ui->mainToolBar->addWidget(btnReplace);
    btnGoto = new QToolButton(this);
    btnGoto->setText("Goto");
    ui->mainToolBar->addWidget(btnGoto);
    ui->mainToolBar->addSeparator();
    btnAbout = new QToolButton(this);
    btnAbout->setText("About");
    connect(btnAbout,SIGNAL(clicked()),this,SLOT(AboutClick()));
    ui->mainToolBar->addWidget(btnAbout);
    // Secondary toolbar
    toolFindReplace = new QToolBar(this);
    toolFindReplace->setGeometry(10,10,250,40);
    toolFindReplace->layout()->setContentsMargins(0,0,0,0);
    toolFindReplace->layout()->setSpacing(0);
    toolFindReplace->setMovable(false);
    textFind = new QLineEdit(this);
    textFind->setMaximumHeight(32);
    textFind->setMinimumHeight(32);
    connect(textFind,SIGNAL(textChanged(QString)),this,SLOT(textFindTextChanged(QString)));
    btnCloseFind = new QToolButton(this);
    btnCloseFind->setText("X");
    btnCloseFind->setStyleSheet("color: #FF0000");
    btnCloseFind->setFixedSize(32,32);
    btnCloseFind->setMaximumSize(32,32);
    btnCloseFind->setMinimumSize(32,32);
    btnFindUp = new QToolButton(this);
    btnFindUp->setMinimumSize(16,32);
    btnFindUp->setMaximumSize(16,32);
    btnFindDown = new QToolButton(this);
    btnFindDown->setMinimumSize(16,32);
    btnFindDown->setMaximumSize(16,32);
    toolFindReplace->addWidget(textFind);
    toolFindReplace->addWidget(btnFindUp);
    toolFindReplace->addWidget(btnFindDown);
    toolFindReplace->addWidget(btnCloseFind);
    toolFindReplace->setHidden(true);
    // Assign objects to JS
    QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    JSBridge bridge;
    connect(ui->webView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),this, SLOT(populateJavaScriptWindowObject()));
    // Setup statusbar
    labelCol = new QLabel(this);
    labelCol->setText("  0  ");
    ui->statusBar->addPermanentWidget(labelCol);
    labelRow = new QLabel(this);
    labelRow->setText("  0  ");
    ui->statusBar->addPermanentWidget(labelRow);
    labelModified = new QLabel(this);
    labelModified->setText("  *  ");
    ui->statusBar->addPermanentWidget(labelModified);
    labelLineMode = new QLabel(this);
    labelLineMode->setText("UNIX");
    ui->statusBar->addPermanentWidget(labelLineMode);
    labelInsert = new QLabel(this);
    labelInsert->setText("INS");
    ui->statusBar->addPermanentWidget(labelInsert);
    listHighlighters = new QComboBox(this);    
    ui->statusBar->addPermanentWidget(listHighlighters);
    listThemes = new QComboBox(this);
    listThemes->addItem("Default");
    ui->statusBar->addPermanentWidget(listThemes);
    // And a blank one for spacing
    QLabel *labelBlank;
    labelBlank = new QLabel(this);
    labelBlank->setText("");
    ui->statusBar->addPermanentWidget(labelBlank,1);
    // Setup file panel
    listOpenFiles = new QListWidget(this);
    listOpenFiles->setMinimumWidth(100);
    listOpenFiles->setMaximumWidth(100);
    listOpenFiles->setContentsMargins(1,1,1,1);
    ui->centralWidget->layout()->addWidget(listOpenFiles);
    connect(listOpenFiles,SIGNAL(itemPressed(QListWidgetItem*)),this,SLOT(listOpenFilesitemActivated(QListWidgetItem*)));
    // Setup cursor position
    QTimer *statusTimer = new QTimer(this);
    statusTimer->setInterval(250);
    connect(statusTimer, SIGNAL(timeout()), this, SLOT(statusTimerUpdate()));
    statusTimer->start();
    // Theming
    themes.InitThemes();
    // Populate dropdowns
    highlighters.InitHighlighters();
    int i;
    listHighlighters->clear();
    for(i=0;i<highlighters.GetCount();i++) {
      listHighlighters->addItem(highlighters.GetHighlighter(i).displayName);
    }
    connect(listHighlighters,SIGNAL(currentIndexChanged(int)),this,SLOT(listHighlightersIndexChanged(int)));
    listThemes->clear();
    for(i=0;i<themes.GetCount();i++) {
        listThemes->addItem(themes.GetTheme(i).displayName);
    }
    SetTheme(28);
    listThemes->setCurrentIndex(28);
    connect(listThemes,SIGNAL(currentIndexChanged(int)),this,SLOT(listThemesIndexChanged(int)));
    PopulateFontCombo();    
    connect(listFonts,SIGNAL(currentIndexChanged(int)),this,SLOT(listFontsIndexChanged(int)));
    textFontSize->setMinimum(6);
    textFontSize->setMaximum(20);
    textFontSize->setValue(10);
    connect(textFontSize,SIGNAL(valueChanged(int)),this,SLOT(textFontSizeValueChanged(int)));
    ui->webView->page()->mainFrame()->evaluateJavaScript("document.getElementById('editor').style.fontSize='10pt';");
    // Create new blank file
    NewFileClick();
}

MainWindow::~MainWindow() {
    delete ui;
}

// http://stackoverflow.com/questions/21411566/base64-encoding-of-string-in-qt-5
QString MainWindow::base64_encode(QString string){
    QByteArray ba;
    ba.append(string);
    return ba.toBase64();
}

QString MainWindow::selectHighlighter(QString filename) {
    QFileInfo ext(filename);
    QString mode;
    int i = -1;
    i = highlighters.GetHighlighterByExt(ext.completeSuffix());
    if(i > -1) {
        mode = highlighters.GetHighlighter(i).displayName;
        //ui->webView->page()->mainFrame()->evaluateJavaScript("editor.getSession().setMode(\"ace/mode/"+mode+"\");");
    }
    else {
        mode = "plain_text";
    }
    return mode;
}

void MainWindow::NewFileClick() {
    QString filename = "Untitled.txt";
    QString contents = "";
    ui->webView->page()->mainFrame()->evaluateJavaScript("editor.getSession().setMode(\"ace/mode/"+selectHighlighter(filename)+"\");");
    ui->webView->page()->mainFrame()->evaluateJavaScript("SetContents(\""+base64_encode(contents)+"\");");
    listHighlighters->setCurrentIndex(highlighters.GetHighlighterByName(selectHighlighter(filename)));
    TOpenFile file;
    file.filename = "";
    file.lines = contents;
    file.modified = false;
    file.cursorPos.setX(1);
    file.cursorPos.setY(1);
    QListWidgetItem *item = new QListWidgetItem();
    item->setText(filename);
    file.item = item;
    listOpenFiles->addItem(file.item);
    listOpenFiles->setItemSelected(file.item,true);
    ui->webView->page()->mainFrame()->setFocus();
    bridge.openFiles.append(file);
    bridge.ActiveFile = bridge.openFiles.count()-1;
}

void MainWindow::populateJavaScriptWindowObject(){
    ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("JSBridge", &bridge);
}

void MainWindow::statusTimerUpdate() {
    ui->webView->page()->mainFrame()->evaluateJavaScript("GetCursorPos();");
    labelCol->setText(QString::number(bridge.cursorPos.x()+1));
    labelRow->setText(QString::number(bridge.cursorPos.y()+1));
}

void MainWindow::listHighlightersIndexChanged(int index) {
    if(index > -1) {
        QString mode = listHighlighters->itemText(index);
        ui->webView->page()->mainFrame()->evaluateJavaScript("editor.getSession().setMode(\"ace/mode/"+mode+"\");");
    }
}

void MainWindow::listThemesIndexChanged(int index) {
    if(index > -1) {
        SetTheme(index);
    }
}

void MainWindow::listFontsIndexChanged(int index) {
    if(index > -1) {
        ui->webView->page()->mainFrame()->evaluateJavaScript("document.getElementById('editor').style.fontFamily='"+listFonts->itemText(index)+"';");
    }
}

void MainWindow::textFontSizeValueChanged(int i) {
    ui->webView->page()->mainFrame()->evaluateJavaScript("document.getElementById('editor').style.fontSize='"+QString::number(i)+"pt';");
}

void MainWindow::SetTheme(int index) {
    ui->statusBar->setStyleSheet("background-color: "+themes.GetTheme(index).bgColour);
    QString toolbarCSS;
    toolbarCSS = "QToolBar QToolButton { font-size: 11pt; border: 1px solid "+themes.GetTheme(index).fgColour+"; color: "+themes.GetTheme(index).fgColour+" }";
    toolbarCSS += "QToolBar QToolButton:hover {border: 1px solid "+themes.GetTheme(index).fgColour+"; background-color: "+themes.GetTheme(index).fgColour+" }";
    toolbarCSS += "QToolBar QToolButton:open { background-color: #D0D0D0; }";
    ui->mainToolBar->setStyleSheet("QToolBar { font-size: 11pt; border: 0px; spacing: 1px; padding: 1px; background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour+"; } "+toolbarCSS);
    toolFindReplace->setStyleSheet("QToolBar { font-size: 11pt; border: 0px; spacing: 1px; padding: 1px; background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour+"; } "+toolbarCSS);
    listThemes->setStyleSheet("background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour);
    listHighlighters->setStyleSheet("background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour);
    labelCol->setStyleSheet("color: "+themes.GetTheme(index).fgColour+"; width: 50px");
    labelRow->setStyleSheet("color: "+themes.GetTheme(index).fgColour+"; width: 50px");
    labelModified->setStyleSheet("color: "+themes.GetTheme(index).fgColour);
    labelLineMode->setStyleSheet("color: "+themes.GetTheme(index).fgColour);
    labelInsert->setStyleSheet("color: "+themes.GetTheme(index).fgColour);
    listOpenFiles->setStyleSheet("background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour);
    ui->webView->page()->mainFrame()->evaluateJavaScript("editor.setTheme(\"ace/theme/"+themes.GetTheme(index).displayName+"\");");    
    listFonts->setStyleSheet("background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour);
    textFontSize->setStyleSheet("background-color: "+themes.GetTheme(index).bgColour+"; color: "+themes.GetTheme(index).fgColour);
}

void MainWindow::OpenFileClick() {
    QString filename = QFileDialog::getOpenFileName(this, "Open file", "", "*.*");
    QFile f(filename);
    QString contents;
    if(f.open(QIODevice::ReadOnly)) {
        QTextStream instream(&f);
        contents.append(instream.readAll());
        f.close();
        listHighlighters->setCurrentIndex(highlighters.GetHighlighterByName(selectHighlighter(filename)));
        TOpenFile file;
        file.filename = filename;
        file.lines = contents;
        file.modified = false;
        file.cursorPos.setX(1);
        file.cursorPos.setY(1);
        QListWidgetItem *item = new QListWidgetItem();
        item->setText(filename);
        file.item = item;
        listOpenFiles->addItem(file.item);
        listOpenFiles->setItemSelected(file.item,true);
        ui->webView->page()->mainFrame()->setFocus();
        bridge.openFiles.append(file);
        bridge.ActiveFile = bridge.openFiles.count()-1;
        ui->webView->page()->mainFrame()->evaluateJavaScript("editor.getSession().setMode(\"ace/mode/"+selectHighlighter(filename)+"\");");
        ui->webView->page()->mainFrame()->evaluateJavaScript("SetContents(\""+base64_encode(contents)+"\");");
    }
}

void MainWindow::SaveFileClick() {
    TOpenFile file;
    QString filename;
    file = bridge.openFiles.at(bridge.ActiveFile);
    if(file.filename == "") {
        filename = QFileDialog::getSaveFileName(this, "Save file", "", "*.*");
    }
    else {
        filename = file.filename;
    }
    if(filename != "") {
        QFile f(filename);
        QVariant contents = ui->webView->page()->mainFrame()->evaluateJavaScript("editor.getSession().getValue();");
        if(f.open(QIODevice::WriteOnly)) {
            QTextStream outstream(&f);
            outstream << contents.toString();
            f.close();
            file.modified = false;
            if(file.filename != filename) file.filename = filename;
            bridge.openFiles.replace(bridge.ActiveFile,file);
            listOpenFiles->item(bridge.ActiveFile)->setText(filename);
        }
    }
}

void MainWindow::listOpenFilesitemActivated(QListWidgetItem* item) {
    int itempos = -1;
    int i;
    TOpenFile file;
    for(i=0;i<bridge.openFiles.count();i++) {
        file = bridge.openFiles.at(i);
        if(file.item == item) itempos = i;
    }
    if(itempos > -1) {
        TOpenFile file = bridge.openFiles.at(itempos);
        listHighlighters->setCurrentIndex(highlighters.GetHighlighterByName(selectHighlighter(file.filename)));
        bridge.ActiveFile = itempos;
        ui->webView->page()->mainFrame()->evaluateJavaScript("editor.getSession().setMode(\"ace/mode/"+selectHighlighter(file.filename)+"\");");
        ui->webView->page()->mainFrame()->evaluateJavaScript("SetContents(\""+base64_encode(file.lines)+"\");");
    }
}

void MainWindow::AboutClick() {
    QString html;
    html = "<p><b style=\"font-size: 14pt\">QAceEdit</b> 0.0<br>\n";
    html.append("&copy;2015 Matthew Hipkin<br>\n");
    html.append("<a href=\"http://www.matthewhipkin.co.uk\" style=\"color: #FF0000\">http://www.matthewhipkin.co.uk</a></p>\n");
    html.append("<p>A programmers' editor.</p>");
    html.append("<p>Powered by <a href=\"http://ace.c9.io\" style=\"color: #FF0000\">Ace embedded editor</a>.</p>");
    html.append("<p>Follow me on twitter <a href=\"http://twitter.com/hippy2094\" style=\"color: #FF0000\">@hippy2094</a></p>");
    html.append("<p><a href=\"https://sourceforge.net/p/qaceedit/\" rel=\"nofollow\"><img alt=\"Download QAceEdit\" src=\"https://sourceforge.net/sflogo.php?type=9&group_id=2457786\"></a></p>");
    QMessageBox::about(this,"About QAceEdit",html);

}

void MainWindow::FindClick() {
    if(toolFindReplace->isHidden()){
        toolFindReplace->setHidden(false);
        int w = toolFindReplace->width();
        int h = toolFindReplace->height();
        QPoint p = btnFind->pos();
        int x = p.x();
        int y = p.y() + btnFind->height();
        toolFindReplace->setGeometry(x,y,w,h);
    }
    else {
        toolFindReplace->setHidden(true);
    }
}

void MainWindow::PopulateFontCombo() {
    QFontDatabase database;
    QString def = QFontDatabase::systemFont(QFontDatabase::FixedFont).defaultFamily();
    foreach (const QString &family, database.families()) {
        if(database.isFixedPitch(family)) {
            listFonts->addItem(family);
            if(family == def) {
                int i = listFonts->findText(family);
                listFonts->setCurrentIndex(i);
            }
        }
    }
}

void MainWindow::textFindTextChanged(QString str) {
    QVariant n;
    n = ui->webView->page()->mainFrame()->evaluateJavaScript("SearchFor('"+textFind->text()+"');");
    qDebug() << n;
}

/* JSBridge */

void JSBridge::SetCursorPos(const QString &col, const QString &row) {
    cursorPos.setX(col.toInt());
    cursorPos.setY(row.toInt());
}

void JSBridge::UpdateFileContents(const QVariant &contents) {
    TOpenFile file = openFiles.at(ActiveFile);
    if(file.lines != contents.toString()) {
        file.lines = contents.toString();
        openFiles.replace(ActiveFile,file);
    }
}

/*QMessageBox msgBox;
msgBox.setText("something");
msgBox.exec();*/
